﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Windows.Input;
using System.Windows.Interop;
using System.Runtime.InteropServices;
using WinKey = System.Windows.Forms.Keys;

namespace Net.Chirash.Twitter.TweetZone.Tools
{
	/// <summary>
	/// グローバルホットキー、グローバルキーフックを提供します
	/// </summary>
	public class GlobalHotkey : IDisposable
	{
		// 注意
		// デバッグ不十分

		/// <summary>
		/// ホットキーを登録します。
		/// 現状登録されたEventHandlerは新スレッドで呼び出されるので、UI処理に注意してください
		/// </summary>
		/// <param name="handler">押されたときに呼び出されるイベントハンドラ。</param>
		public void RegisterKey(Key hotkey, ModKey modkey, EventHandler handler)
		{
			WinKey k = (WinKey)KeyInterop.VirtualKeyFromKey(hotkey);
			HotKeyAdd(k, modkey, handler);
		}

		[DllImport("user32.dll")]
		extern static int RegisterHotKey(IntPtr HWnd, int ID, ModKey MOD_KEY, WinKey KEY);

		[DllImport("user32.dll")]
		extern static int UnregisterHotKey(IntPtr HWnd, int ID);

		const int WM_HOTKEY = 0x0312;
		Dictionary<int, EventHandler> Hotkeys;
		HwndSource source;
		IntPtr hWnd;

		private void HotKeyAdd(WinKey k, ModKey modkey, EventHandler handler)
		{
			int i = Hotkeys.Count;
			while (true)
			{
				if (RegisterHotKey(hWnd, i, modkey, k) != 0)
				{
					Hotkeys.Add(i, handler);
					break;
				}
				i++;
			}
		}

		private void HotKeyDestroyAll()
		{
			foreach (var id in Hotkeys)
			{
#if DEBUG
				if (UnregisterHotKey(hWnd, id.Key) != 0)
				{
					throw new Exception("DEBUG:ホットキー消去に失敗");
				}
#else
				UnregisterHotKey(hWnd, id.Key);
#endif
			}
			Hotkeys = null;
			if (!source.IsDisposed)
				source.Dispose();
		}

		private IntPtr WndProc(IntPtr hwnd, int msg, IntPtr wParam, IntPtr lParam, ref bool handled)
		{
			if (msg == WM_HOTKEY)
			{
				try
				{
					Hotkeys[(int)wParam].BeginInvoke(this, EventArgs.Empty, null, null);
				}
				catch (Exception)
				{
				}
				handled = true;
			}
			return IntPtr.Zero;
		}

		public enum ModKey { Alt, Ctrl, Shift }

		#region Dispose-Finalize Pattern

		private bool __disposed = false;
		public void Dispose()
		{
			GC.SuppressFinalize(this);
			this.Dispose(true);
		}

		protected void Dispose(bool disposing)
		{
			if (__disposed) return;
			if (disposing)
			{
				
			}
			
			HotKeyDestroyAll();

			//base.Dispose();
			__disposed = true;
		}

		~GlobalHotkey()
		{
			this.Dispose(false);
		}
		#endregion
	}
}
